package com.example.demo21_spring_session.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;

import javax.annotation.Resource;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private FindByIndexNameSessionRepository<Session> sessionRepository;

    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    @Bean
    protected UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("wangnian").password("123").roles("admin").build());
        return inMemoryUserDetailsManager;
    }

    /**
     * 会话注册表的维护默认是由 SessionRegistryImpl 来维护的，而 SessionRegistryImpl 的维护就是基于内存的维护。
     * 现在我们虽然启用了 Spring Session + Redis 做 Session 共享，但是 SessionRegistryImpl 依然是基于内存来维护的，
     * 所以我们要修改 SessionRegistry 的实现。
     *
     * 修改方式也很简单，实际上 Spring Session 为我们提供了对应的实现类 SpringSessionBackedSessionRegistry，具体配置如下：
     */
    @Bean
    SessionRegistry sessionRegistry(){
        return new SpringSessionBackedSessionRegistry<>(sessionRepository);
    }

    /**
     * 使用 SpringSessionBackedSessionRegistry 后不需要注册 HttpSessionEventPublisher，否则会报错
     */
//    @Bean
//    HttpSessionEventPublisher httpSessionEventPublisher(){
//        return new HttpSessionEventPublisher();
//    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and().formLogin()
                .and().csrf().disable()
                .sessionManagement()
                .maximumSessions(1)
                // 使用分布式集群下 spring session 提供的 SessionRegistry，不能再使用默认基于内存的 SessionRegistryImpl
                .sessionRegistry(sessionRegistry()).maxSessionsPreventsLogin(true);
    }
}
